package com.oitsm.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.Map;
import java.util.HashMap;

/**
 * property 工具,提供一些反射的方法
 * @author Arab Runner
 * Jun 25, 2011 11:56:42 AM
 */
public final class PropertyUtils {
	/**
	 * @param <T>
	 * @param bean
	 * @param name
	 * @return
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	@SuppressWarnings("unchecked")
	public static <T> Object getProperty(T bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if (bean instanceof Map) {
            return ((Map) bean).get(name);
		}
		
		Method readMethod = getReadMethod(bean, name);		
	    return readMethod.invoke(bean);
	}
	
	/**
	 * @param <T>
	 * @param bean
	 * @param name
	 * @param value
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	@SuppressWarnings("unchecked")
    public static <T> void setProperty(T bean, String name, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if (bean instanceof Map) {
			((Map) bean).put(name, value);
			return;
		}
		Method writeMethod = getWriteMethod(bean, name);
		
		if (null != value) {
			writeMethod.invoke(bean, value);
		}
	}
    
    /**
     * @param <T>
     * @param bean
     * @param name
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    public static <T> Method getReadMethod(T bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
	    if (descriptor == null) {
	        throw new NoSuchMethodException("Unknown property '" + name + "'");
	    }
	    
	    Method readMethod = descriptor.getReadMethod();
	    if (readMethod == null) {
	        throw new NoSuchMethodException("Property '" + name + "' has no getter method");
	    }
	    
	    return readMethod;
	}
	
    /**
     * @param <T>
     * @param bean
     * @param name
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
	public static <T> Method getWriteMethod(T bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
	    if (descriptor == null) {
	        throw new NoSuchMethodException("Unknown property '" + name + "'");
	    }
	    
        Method writeMethod = descriptor.getWriteMethod();
        if (writeMethod == null) {
            throw new NoSuchMethodException("Property '" + name + "' has no setter method");
        }

        return writeMethod;
	}
	
	/**
	 * @param <T>
	 * @param bean
	 * @param name
	 * @return
	 */
	public static <T> PropertyDescriptor getPropertyDescriptor(T bean, String name) {
        PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
        if (descriptors != null) {            
            for (int i = 0; i < descriptors.length; i++) {
                if (name.equalsIgnoreCase(descriptors[i].getName()))
                    return (descriptors[i]);
            }
        }        
        return null;		
	}
	
	/**
	 * @param <T>
	 * @param bean
	 * @return
	 */
    public static <T> PropertyDescriptor[] getPropertyDescriptors(T bean) {
    	Class<?> clazz = bean.getClass();
    	String className = clazz.getName();
    	
		PropertyDescriptor[] descriptors = null;
		synchronized (descriptorsCache) {
			descriptors = descriptorsCache.get(className);
        }   

        if (descriptors != null) {
            return descriptors;
        }
    	
        // Introspect the bean and cache the generated descriptors
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            return null;
        }
        
        descriptors = beanInfo.getPropertyDescriptors();
        if (descriptors == null) {
            return null;
        }
        
        synchronized (descriptorsCache) {
    		descriptorsCache.put(className, descriptors);
        }
        
        return descriptors;
    }   

	/**
	 * @param <T>
	 * @param bean
	 * @param name
	 * @return
	 */
    public static <T> Class<?> getPropertyType(T bean, String name) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if (bean == null) {
		    throw new IllegalArgumentException("No bean specified");
		}
		if (name == null) {
		    throw new IllegalArgumentException("No name specified");
		}

		PropertyDescriptor descriptor = getPropertyDescriptor(bean, name);
		if (descriptor == null) {
			return null;
		}
		return descriptor.getPropertyType();
    }
    
    /**
     */
    protected static final Map<String, PropertyDescriptor[]> descriptorsCache = new HashMap<String, PropertyDescriptor[]> ();
}
